#include "StdAfx.h"
#include "HeaderWriter.h"


#include "File.h"

namespace las{

	HeaderWriter::HeaderWriter( File *pFile, std::uint64_t count, Header const& header )
		:m_pFile(pFile)
		,m_PointCount(count)
		,m_header(header)
	{
	}


	HeaderWriter::~HeaderWriter(void)
	{
	}

	Header & HeaderWriter::GetHeader() const
	{
		return (Header)m_header;
	}

	void HeaderWriter::Write()
	{
		
		uint8_t n1 = 0;
		uint16_t n2 = 0;
		uint32_t n4 = 0;

		bool bAppendMode = false;

		m_pFile->Seek(0, File::eBegin);

		if (m_pFile->IsEmpty()) 
		{
			bAppendMode = true;
		}

		// If we are in append mode, we are not touching *any* VLRs. 
		if (bAppendMode) 
		{
			std::uint64_t points = m_pFile->Size() - m_header.GetDataOffset();
			std::uint64_t count = points / m_header.GetPointRecordLength();

			m_PointCount = static_cast<uint32_t>(count);

			m_pFile->Seek(0, File::eBegin);
		} 
		else 
		{
			m_header.SetRecordCount(0);

			int32_t difference = m_header.GetDataOffset() - m_header.GetHeaderSize();

			if (difference <= 0) 
			{
				int32_t d = abs(difference);
				if (m_header.GetVersionMajor() == 1 && m_header.GetVersionMinor()  ==  0) 
				{
					// Add the two extra bytes for the 1.0 pad
					d = d + 2;
				}
				m_header.SetDataOffset(m_header.GetDataOffset() + d );
			}

		}


		// 1. File Signature
		std::string const filesig(m_header.GetFileSignature());
		m_pFile->Write((void*)filesig.c_str(), 4);

		// 2. File SourceId / Reserved
			n2 = m_header.GetSourceID();
			m_pFile->Write(&n2, 2);
			n2 = m_header.GetGlobalEncoding();
			m_pFile->Write(&n2, 2);

		// 3-6. GUID data
		uint32_t d1 = 0;
		uint16_t d2 = 0;
		uint16_t d3 = 0;
		uint8_t d4[8] = { 0 };
		GUID g = m_header.GetProjectID();
		m_pFile->Write(&g.Data1, 4);
		m_pFile->Write(&g.Data2, 2);
		m_pFile->Write(&g.Data3, 2);
		m_pFile->Write(g.Data4, 4);

		// 7. Version major
		n1 = m_header.GetVersionMajor();
		m_pFile->Write(&n1, 1);

		// 8. Version minor
		n1 = m_header.GetVersionMinor();
		m_pFile->Write(&n1, 1);

		// 9. System ID
		std::string sysid(m_header.GetSystemID());
		m_pFile->Write((void*)sysid.c_str(), 32);

		// 10. Generating Software ID
		std::string softid(m_header.GetSoftwareID());
		m_pFile->Write((void*)softid.c_str(), 32);

		// 11. Flight Date Julian
		n2 = m_header.GetCreatDOY();
		m_pFile->Write(&n2, 2);

		// 12. Year
		n2 = m_header.GetCreateYear();
		m_pFile->Write(&n2, 2);

		// 13. Header Size
		n2 = m_header.GetHeaderSize();
		m_pFile->Write(&n2, 2);

		// 14. Offset to data
		n4 = m_header.GetDataOffset();        
		m_pFile->Write(&n4, 4);

		// 15. Number of variable length records
		n4 = m_header.GetRecordCount();
		m_pFile->Write(&n4, 4);

		// 16. Point Data Format ID
		n1 = static_cast<uint8_t>(m_header.GetPointFormat());
		m_pFile->Write(&n1, 1);

		// 17. Point Data Record Length
		n2 = m_header.GetPointRecordLength();
		m_pFile->Write(&n2, 2);

		// 18. Number of point records
		// This value is updated if necessary, see UpdateHeader function.
		n4 = m_header.GetPointRecordsCount();
		m_pFile->Write(&n4, 4);
/*
		// 19. Number of points by return
		std::vector<uint32_t>::size_type const srbyr = 5;
		std::vector<uint32_t> const& vpbr = m_header.GetPointRecordsByReturn();
		// TODO: fix this for 1.3, which has srbyr = 7;  See detail/reader/header.cpp for more details
		// assert(vpbr.size() <= srbyr);
		uint32_t pbr[srbyr] = { 0 };
		std::copy(vpbr.begin(), vpbr.begin() + srbyr, pbr); // FIXME: currently, copies only 5 records, to be improved
		detail::write_n(m_ofs, pbr, sizeof(pbr));

		// 20-22. Scale factors
		detail::write_n(m_ofs, m_header.GetScaleX(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetScaleY(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetScaleZ(), sizeof(double));

		// 23-25. Offsets
		detail::write_n(m_ofs, m_header.GetOffsetX(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetOffsetY(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetOffsetZ(), sizeof(double));

		// 26-27. Max/Min X
		detail::write_n(m_ofs, m_header.GetMaxX(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetMinX(), sizeof(double));

		// 28-29. Max/Min Y
		detail::write_n(m_ofs, m_header.GetMaxY(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetMinY(), sizeof(double));

		// 30-31. Max/Min Z
		detail::write_n(m_ofs, m_header.GetMaxZ(), sizeof(double));
		detail::write_n(m_ofs, m_header.GetMinZ(), sizeof(double));

		// If WriteVLR returns a value, it is because the header's 
		// offset is not large enough to contain the VLRs.  The value 
		// it returns is the number of bytes we must increase the header
		// by in order for it to contain the VLRs.  We do not touch VLRs if we 
		// are in append mode.

		if (!bAppendMode) 
		{
			WriteVLRs();

			// Write the 1.0 pad signature if we need to.
			WriteLAS10PadSignature(); 

		}
		// If we already have points, we're going to put it at the end of the file.  
		// If we don't have any points,  we're going to leave it where it is.
		if (m_pointCount != 0)
		{
		m_ofs.seekp(0, std::ios::end);
		}
		else
		{
		m_ofs.seekp(m_header.GetDataOffset(), std::ios::beg);
		}
		*/
	}

}
